#include "stdafx.h"
#include "ylMemoryStream.h"

#include <fstream>
#include <exception>

using namespace yoctolog;

MemoryStream::MemoryStream(unsigned int uiStartingSize)
	: m_pData(NULL)
	, m_iDataPos(0)
	, m_iUsedSize(0)
{
	ResizeBuffer(uiStartingSize);
}


MemoryStream::MemoryStream(unsigned char *pData, unsigned int uiSize)
	: m_pData(NULL)
	, m_iDataPos(0)
	, m_iUsedSize(uiSize)
{
	ResizeBuffer(uiSize);
	memcpy(m_pData, pData, uiSize);
}


MemoryStream::~MemoryStream()
{
	if (m_pData)
	{
		delete [] m_pData;
		m_pData = NULL;
	}
}


void MemoryStream::Clear()
{
	m_iUsedSize = 0;
	m_iDataPos = 0;
}


void MemoryStream::operator >>(char * &szValue) const
{
	szValue = static_cast<char *>(&m_pData[m_iDataPos]);
	// +1 for NULL-terminator
	m_iDataPos += (int)(strlen(szValue) + 1);
}


void MemoryStream::operator >>(std::string &strValue) const
{
	strValue = static_cast<char *>(&m_pData[m_iDataPos]);
	// +1 for NULL-terminator
	m_iDataPos += (int)(strValue.length() + 1);
}


void MemoryStream::operator >>(bool &bValue) const
{
	bValue = static_cast<bool>(m_pData[m_iDataPos] != 0);
	m_iDataPos += sizeof(bool);
}


void MemoryStream::operator <<(const char* szValue)
{
	// +1 for NULL-terminator
	int iStrLen = (int)strlen(szValue) + 1;

	Write(szValue, iStrLen * sizeof(char));
}


void MemoryStream::operator <<(char* szValue)
{
	(*this) << ((const char*)szValue);
}


void MemoryStream::operator <<(const std::string &strValue)
{
	(*this) << strValue.c_str();
}


void MemoryStream::operator <<(const bool &bValue)
{
	Write(&bValue, sizeof(bool));
}


bool MemoryStream::ResizeBuffer(unsigned int uiNewSize)
{
	char *pOldData = NULL;
	if (m_pData)
	{
		try
		{
			pOldData = new char[m_iDataSize];
		} catch ( std::bad_alloc& )
		{
			return false;
		}
		memcpy(pOldData, m_pData, m_iDataSize);
		delete [] m_pData;
	}

	try
	{
		m_pData = new char[uiNewSize];
	}
	catch ( std::bad_alloc& )
	{
		if( pOldData )
		{
			delete [] pOldData;
		}
		return false;
	}

	if (pOldData)
	{
		// Copy the old data back in
		memcpy(m_pData, pOldData, m_iDataSize);
		// Delete the temporary data
		delete [] pOldData;
	}

	m_iDataSize = uiNewSize;
	return true;
}


void MemoryStream::Write(const void * pData, int iSize)
{
	int iNewSizeRequired = m_iDataPos + iSize;
	int iNewSize = m_iDataSize;
	while( iNewSizeRequired >= iNewSize )
	{
		iNewSize *= 2;
		if( iNewSize > iNewSizeRequired )
		{
			bool bSucceeded = ResizeBuffer(iNewSize);
			// We failed to resize the buffer - just return without writing
			if( !bSucceeded )
				return;
		}
	}
	m_iUsedSize += iSize;

	memcpy(&m_pData[m_iDataPos], pData, iSize);

	m_iDataPos += iSize;
}


void MemoryStream::WriteToFile(const std::string &strFilename)
{
	std::ofstream fStream;
	fStream.open(strFilename.c_str(), std::ios::out | std::ios::binary);

	fStream.write(m_pData, m_iDataPos);

	fStream.flush();
	fStream.close();
}


void MemoryStream::LoadFromFile(const std::string &strFilename)
{
	std::ifstream fStream;

	fStream.open(strFilename.c_str(), std::ios::in | std::ios::binary);

	m_iDataPos = 0;
	m_iDataSize = 0;
	m_iUsedSize = 0;
	while (!fStream.eof())
	{
		fStream.read(&m_pData[m_iDataSize], 1);
		++m_iDataSize;
	}

	fStream.close();
}
